#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define SIZE 1024
#define MAX_ARGC 64     // max size of argv[]
#define SEP " " 

char* argv[MAX_ARGC];
char my_pwd[SIZE];        // BuildinCmd()
char my_env[SIZE];        // BuildinCmd()
int last_return_code = 0;   // record the last exec return code

const char* HostName()
{
    const char* hostname = getenv("HOSTNAME");
    if(hostname) {return hostname;}
    return "None";
}

const char* UserName()
{
    const char* username = getenv("USER");
    if(username) {return username;}
    return "None";
}

const char* CurrentWorkDir()
{
    const char* dirname = getenv("PWD");
    if(dirname) {return dirname;}
    return "None";
}

char* Home()    // used by BuildinCmd()
{
    return getenv("HOME");
}

int Interactive(char out[], int size)
{
    printf("[%s@%s %s]$ ", UserName(), HostName(), CurrentWorkDir());
    fgets(out, size, stdin);    // size -> fgets can recept max word length
    out[strlen(out) - 1] = 0;   // set out[] word '\n' to '\0' which been set from fgets()
    return strlen(out);
}

void Split(char in[])       // split str like "ls -l -a" to "ls","-l","-a"
{
    int i = 0;
    char tmp[strlen(in)];
    strcpy(tmp, in);
    argv[i++] = strtok(tmp, SEP);    // split str when pos = SEP 
    while(argv[i++] = strtok(NULL, SEP));       // strtok(NULL, SEP) can record last pos
    
    // set 'ls' to print with color
    if(strcmp(argv[0], "ls") == 0)
    {
        argv[i-1] = (char*)"--color";
        argv[i] = NULL;
    }
}

int BuildinCmd()
{
    int ret = 0;
    // check whether being a Bulid-in Command
    if(strcmp("cd", argv[0]) == 0)
    {
        ret = 1;

        char* target = argv[1];
        if(!target){target = Home();}       // handle case of "cd" not "cd XXX"
        chdir(target);

        char tmp[SIZE];     // write buffer
        getcwd(tmp, SIZE);
        snprintf(my_pwd, SIZE, "PWD=%s", tmp);
        putenv(my_pwd);
    }
    else if(strcmp("export", argv[0]) == 0)
    {
        ret = 1;

        if(argv[1])
        {
            strcpy(my_env, argv[1]);
            putenv(my_env);
        }
    }
    else if(strcmp("echo", argv[0]) == 0)
    {
        ret = 1;

        if(!argv[1])
        {
            printf("\n");
        }
        else{
            if(argv[1][0] == '$')
            {
                if(argv[1][1] == '?')
                {
                    printf("%d\n", last_return_code);
                    last_return_code = 0;
                }
                else{
                    char* e = getenv(argv[1] + 1);
                    if(e) { printf("%s\n", e); }
                }
            }
            else 
            {
                printf("%s\n", argv[1]);
            }
        }
    }

    return ret;
}

void Execute()
{
    pid_t id = fork();
    if(id == 0)     // child_process
    {
        int error = execvp(argv[0], argv);
        exit(error);
    }
    int status = 0;     // record return error code
    pid_t rid = waitpid(id, &status, 0);
    if(rid == id) { last_return_code = WEXITSTATUS(status); }
    printf("last_return_code = %d\n", last_return_code);
}


int main()
{
    while(true)
    {
        char commandline[SIZE];
        // 1.print command prompt, get cin form USER
        int effective_length = Interactive(commandline, SIZE);
        if(effective_length == 0)       // handle the case which the input is '\n' 
        {
            continue;
        }
            printf("commandline:%s\n", commandline);
        // 2. split to commandline 
        Split(commandline);
        // 3. handle the Built-in command 
        int n = BuildinCmd();
        if(n == 1) { continue; }
        // 4. run the command 
        Execute();
    }

    return 0;
}

